package client;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import utils.ClientServerEnum;
import utils.ClientServerMessage;
import utils.Hashes;

import common.ClientEntity;
import common.Entity;

import java.io.*;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Client extends ClientEntity
{	
	public static final String STOP = "stop client";
	
	byte[] lock;
	List<String> serverIps;  // static server ips;
	SSLSocketFactory sslsocketfactory;
	Map<Integer, List<Entity>> serverGroups; 
	List<ClientListener> clientListeners;
	
	public Client(int id, String pass) {
		super(id, pass);
		serverIps = new ArrayList<String>();
		clientListeners = new ArrayList<ClientListener>();
		serverGroups = new HashMap<Integer, List<Entity>>();
		lock = new byte[0];
	}
	
	public static void main(String[] arstring)
	{
		int id = Integer.parseInt(arstring[0]);
		Client c = getClient(id);
		c.setStaticServerIps();
		c.loopLogin();
	}
	public void setServerIps(List<String> serverIps) {
		this.serverIps = serverIps;
	}
	public void removeListener(ClientListener cl) {
		clientListeners.remove(cl);
		while(clientListeners.isEmpty()) {
			if(login()) {
				continue;
			}
		}
	}
	public void readFromCmdLine() throws Exception{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String msg;
		try {
			while((msg = br.readLine()) != STOP) {
				String s1 = msg.substring(0, msg.indexOf(' '));
				String s2 = msg.substring(msg.indexOf(' ') +1);
				if(!sendMsg(ClientServerMessage.getContentOutput(ClientServerEnum.SEND_MESSAGE.ordinal(), 
						s1, s2))) {
					throw new Exception();
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("handling disconnection");
			throw e;
		}
	}
	
	private boolean sendMsg(String s) {
		for(int i = 0; i < clientListeners.size(); i ++) {
			ClientListener cl = clientListeners.get(i);
			if(cl.sendMsg(s)) {
				return true;
			}
		}
		return false;
	}

	private List<String> setStaticServerIps() {
		try {
			BufferedReader br = new BufferedReader(new FileReader("StaticServerList"));
			String s = null;
			List<String> serverIPs = new ArrayList<String>();
			try {
				while((s = br.readLine()) != null) {
					String[] conts = s.split(" ");
					String serverIp = conts[1];
					serverIPs.add(serverIp);
				}
			} catch (IOException e) {

			}
			serverIps = serverIPs;
			return serverIPs;
		} catch (FileNotFoundException e) {

		}
		return null;
	}
	
	private static Client getClient(int id) {
		try {
			BufferedReader br = new BufferedReader(new FileReader("StaticClientList"));
			String pass = null;
			String s = null;
			while((s = br.readLine()) != null) {
				String[] conts = s.split(" ");
				int clientID = Integer.parseInt(conts[0]);
				if(id == clientID) {
					pass = conts[1];
					break;
				}
			}
			if(pass == null) {
				System.out.println("Error, client id is not found in StaticClientList");
				return null;
			}
			return new Client(id, pass);
		} catch (NumberFormatException e) {

		} catch (IOException e) {

		}
		return null;
	}
	
	private boolean retrieveServerGroups(String ip) {
		try
		{
			sslsocketfactory = (SSLSocketFactory) SSLSocketFactory
					.getDefault();
			SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(ip, 9999);
			final String[] enabledCipherSuites = { "SSL_DH_anon_WITH_RC4_128_MD5" };
			sslsocket.setEnabledCipherSuites(enabledCipherSuites);
			BufferedWriter bufferedwriter = new BufferedWriter(
					new OutputStreamWriter(sslsocket.getOutputStream()));
			BufferedReader br = new BufferedReader(
					new InputStreamReader(sslsocket.getInputStream()));
			
			int request= ClientServerEnum.LIST_OF_SERVERS.ordinal();
			bufferedwriter.write("" + request + "\n");
			bufferedwriter.flush();
			
			serverGroups = new HashMap<Integer, List<Entity>>();
			String serverGroup = null;
			int groupId = 0;
			while(!(serverGroup = br.readLine()).equals("END")){
				if(serverGroup.length() == 0) {
					groupId++;
					continue;
				}
				serverGroups.put(groupId, parseServerIp(serverGroup));
				groupId ++;
			}
			sslsocket.close();
			return true;
		} catch (Exception exception){
			System.out.println("wrong server, try next one");
			return false;
		}
	}
	
	private List<Entity> parseServerIp(String s){
		List<Entity> res = new ArrayList<Entity>();
		String[] servers = s.split("\t");
		for(String server : servers) {
			String id = server.split(" ")[0];
			String ip = server.split(" ")[1];
			res.add(new Entity(Integer.parseInt(id), ip));
		}
		return res;
	}
	
	public void loopLogin() {
		boolean loggedIn = false;
		while(!loggedIn) {
			for(String ip : serverIps) {
				if(retrieveServerGroups(ip)) {
					loggedIn = true;
					break;
				}
			}
		}
		loggedIn = login();
	}
	
	@SuppressWarnings("finally")
	private boolean login() {
		boolean success = false;
		List<Integer> groupOrder = Hashes.getGroupIdListFromClientId(this.id);
		
		for(int i = 0; i < groupOrder.size() && !success; i ++) {
			int groupId = groupOrder.get(i);
			if( serverGroups.containsKey(groupId)) {
				List<Entity> serverInGroup = serverGroups.get(groupId);
				for(Entity server : serverInGroup) {
					if(attemptToLogin(server)) {
						success = true;
					}
				}
			}			
		}
		if(!success) {
			return false;
		}
		
		try {
			while(true) {
				readFromCmdLine();
			}
		} catch(Exception e) {
			return false;  //returns false so that loopLogin will reconnect
		} finally {
			return true;
		}
	}
	
	private boolean attemptToLogin(Entity et) {
		String ip = et.getIp();
		try {
			SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(
					ip, 9999);
			final String[] enabledCipherSuites = { "SSL_DH_anon_WITH_RC4_128_MD5" };
			sslsocket.setEnabledCipherSuites(enabledCipherSuites);
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sslsocket.getOutputStream()));
			String loginMessage =  ClientServerMessage.getLoginMessage(this);
			bw.write(loginMessage);
			bw.flush();
			BufferedReader br = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
			String responseStr = null;
		    responseStr = br.readLine();
			ClientServerMessage response = new ClientServerMessage(responseStr);
			if(response.getMessageType() == ClientServerEnum.SIGNED_ON) {
				System.out.println("signed on at server " + et.getId());
				try {
					ClientListener cl = new ClientListener(this, sslsocket);
					clientListeners.add(cl);
					cl.start();
				}catch(Exception e){
					return false;
				}
				return true;
			} else {
				return false;
			}
		} catch (UnknownHostException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
	}
	
	public byte[] getLock() {
		return lock;
	}
}
